home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / Onboard / IconPalette.py < prev    next >
Text File  |  2009-10-01  |  11KB  |  267 lines

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. # Copyright ┬⌐ 2008 Francesco Fumanti <francesco.fumanti@gmx.net>
  5. #
  6. # This file is part of Onboard.
  7. #
  8. # Onboard is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 3 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # Onboard is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  20.  
  21. from os.path import join
  22.  
  23. import gtk
  24. import gobject
  25.  
  26. ### Logging ###
  27. import logging
  28. _logger = logging.getLogger("IconPalette")
  29. ###############
  30.  
  31. ### Config Singleton ###
  32. from Onboard.Config import Config
  33. config = Config()
  34. ########################
  35.  
  36. DRAG_THRESHOLD = 8 # in pixels; 8 is the default gtk value
  37.  
  38. RESIZE_AREA_SIZE = 20 # Use a fictive but sensible size
  39.  
  40. class IconPalette(gtk.Window):
  41.     """
  42.     Class that creates a movable and resizable floating window without
  43.     decorations.  The window shows the icon of onboard scaled to fit to the
  44.     window and a resize grip that honors the desktop theme in use.
  45.  
  46.     Onboard offers an option to the user to make the window appear
  47.     whenever the user hides the onscreen keyboard.  The user can then
  48.     click on the window to hide it and make the onscreen keyboard
  49.     reappear.
  50.     """
  51.  
  52.     """Store whether the last click event was by button 1."""
  53.     _button1_pressed = False 
  54.  
  55.     """
  56.     Needed in the motion-notify-event callback to ignore little movements
  57.     and in the button-release-event callback to determine whether a click
  58.     happened.
  59.     """
  60.     _button1_press_x_pos = 0     
  61.     _button1_press_y_pos = 0
  62.  
  63.     """When configuring: whether it is a resize or a move."""
  64.     _is_press_in_resize_area = False 
  65.  
  66.     def __init__(self):
  67.         gtk.Window.__init__(self)
  68.         _logger.debug("Entered in __init__")
  69.  
  70.         # create iconpalette starting by an inherited gtk.window
  71.         self.set_accept_focus(False)
  72.         self.set_keep_above(True)
  73.         self.set_decorated(False)
  74.         self.set_property('skip-taskbar-hint', True)
  75.         self.set_resizable(True)
  76.         self.set_geometry_hints(self,
  77.             20, 20,            # minimum width, height
  78.             -1, -1,            # maximum width, height
  79.             config.icp_width,  # base width
  80.             config.icp_height, # base height
  81.              1,  1,            # width, height resize increment
  82.             -1, -1)            # min, max aspect ratio
  83.         self.set_border_width(0)
  84.         self.set_app_paintable(True)
  85.  
  86.         # default coordinates of the iconpalette on the screen
  87.         self.move(config.icp_x_position, config.icp_y_position)
  88.         self.resize(config.icp_width, config.icp_height)
  89.  
  90.         # set up attributes for content of icon palette
  91.         self.image_pixbuf = gtk.gdk.pixbuf_new_from_file( \
  92.                 join(config.install_dir, "data/onboard.svg"))
  93.         self.icp_image = gtk.Image()
  94.         self.image_box = gtk.Fixed()
  95.         self.image_box.put(self.icp_image, 0, 0)
  96.         self.add(self.image_box)
  97.  
  98.         # set up event handling
  99.         self.add_events(gtk.gdk.BUTTON_PRESS_MASK
  100.                       | gtk.gdk.BUTTON_RELEASE_MASK
  101.                       | gtk.gdk.BUTTON1_MOTION_MASK)
  102.         self.connect("button-press-event", self._cb_start_click_or_move_resize)
  103.         self.connect("motion-notify-event", self._cb_move_resize_action)
  104.         self.connect("button-release-event", self._cb_click_action)
  105.         self.connect("configure-event", self._cb_scale_and_save)
  106.         self.connect("expose-event", self._cb_draw_resize_grip)
  107.  
  108.         config.icp_size_change_notify_add(self.resize)
  109.         config.icp_position_change_notify_add(self.move)
  110.  
  111.         gobject.signal_new("activated", IconPalette, gobject.SIGNAL_RUN_LAST,
  112.                 gobject.TYPE_BOOLEAN, ())
  113.         _logger.debug("Leaving __init__")
  114.  
  115.     def _is_click_in_resize_area(self, event):
  116.         """Check whether the event occurred in the resize grip."""
  117.         _logger.debug("Entered in _is_click_in_resize_area")
  118.         response = False
  119.         if config.icp_width - RESIZE_AREA_SIZE < event.x \
  120.            and event.x < config.icp_width \
  121.            and config.icp_height - RESIZE_AREA_SIZE < event.y \
  122.            and event.y < config.icp_height:
  123.             response = True
  124.         return response
  125.  
  126.     def _cb_start_click_or_move_resize(self, widget, event):
  127.         """
  128.         This is the callback for the button-press-event.
  129.  
  130.         It initiates the variables used during the moving and resizing
  131.         of the IconPalette window; and used to determine whether the
  132.         button-press and button-release sequence can be considered a
  133.         button click.
  134.         """
  135.         _logger.debug("Entered in _cb_start_click_or_move_resize()")
  136.         if not event.button == 1: # we are only interested in button 1 events
  137.             return
  138.         self._button1_pressed = True
  139.         _logger.debug("passed self._button1_pressed = True")
  140.         self._is_press_in_resize_area = self._is_click_in_resize_area(event)
  141.  
  142.         # needed to check whether movement is below threshold
  143.         self._button1_press_x_pos = event.x_root 
  144.         self._button1_press_y_pos = event.y_root
  145.  
  146.     def _cb_move_resize_action(self, widget, event):
  147.         """
  148.         This is the callback for the motion-notify-event.
  149.  
  150.         Depending on whether the button press occurred on the content of
  151.         the window or on the resize grip, it asynchronuously calls
  152.         gtk.Window.begin_move_drag() or gtk.Window.begin_resize_drag().
  153.         """
  154.         _logger.debug("Entered in _cb_move_resize_action()")
  155.         # we are only interested in button 1 events
  156.         if not self._button1_pressed: 
  157.             return
  158.         _logger.debug("passed _button1_pressed")
  159.         if abs(event.x_root - self._button1_press_x_pos) < DRAG_THRESHOLD \
  160.         and abs(event.y_root - self._button1_press_y_pos) < DRAG_THRESHOLD:
  161.             return  # we ignore movements smaller than the threshold
  162.         _logger.debug("passed  ignore small movement")
  163.         if self._is_press_in_resize_area:
  164.             _logger.debug("Entering begin_resize_drag()")
  165.             self.begin_resize_drag(gtk.gdk.WINDOW_EDGE_SOUTH_EAST, 1,
  166.                                    int(event.x_root), int(event.y_root), 
  167.                                    event.time)
  168.         else:
  169.             _logger.debug("Entering begin_move_drag()")
  170.             self.begin_move_drag(1, int(event.x_root), int(event.y_root), 
  171.                                  event.time)
  172.         # REMARK: begin_resize_drag() and begin_move_drag() seem to run
  173.         # asynchronously: in other words, if there is code after them, it will
  174.         # in most cases run before the move or the resize have finished.
  175.         # To execute code after begin_resize_drag() and begin_move_drag(),
  176.         # the callback of the configure-event can probably be used.
  177.  
  178.     def _cb_scale_and_save(self, event, user_data):
  179.         """
  180.         This is the callback for the configure-event.
  181.  
  182.         It saves the geometry of the IconPalette window to the gconf keys
  183.         by using the Config singleton.
  184.  
  185.         It scales the content of the IconPalette window to make it fit to
  186.         the new window size.
  187.         """
  188.         _logger.debug("Entered in _cb_scale_and_save()")
  189.         if self.get_property("visible"):
  190.             # save size and position
  191.             config.icp_width, config.icp_height = self.get_size()
  192.             config.icp_x_position, config.icp_y_position = self.get_position()
  193.  
  194.             # draw content (does not draw resize grip)
  195.             scaled_image_pixbuf = self.image_pixbuf.scale_simple(config.icp_width, \
  196.                                                       config.icp_height, \
  197.                                                       gtk.gdk.INTERP_BILINEAR)
  198.             resize_grip_area = scaled_image_pixbuf.subpixbuf( \
  199.                                         config.icp_width - RESIZE_AREA_SIZE, \
  200.                                         config.icp_height - RESIZE_AREA_SIZE, \
  201.                                         RESIZE_AREA_SIZE, RESIZE_AREA_SIZE)
  202.             resize_grip_area.fill(0x00000000) # make transparent
  203.             self.icp_image.set_from_pixbuf(scaled_image_pixbuf)
  204.             del resize_grip_area
  205.             del scaled_image_pixbuf
  206.         # REMARK: After clicking on the iconpalette, another configure event
  207.         # arrives after the iconpalette has been hidden and a wrong position
  208.         # gets stored in the config keys. Therefore the visibility check.
  209.  
  210.     def _cb_draw_resize_grip(self, event, user_data):
  211.         """
  212.         This is the callback for the expose-event.
  213.  
  214.         It is responsible for drawing the resize grip.
  215.         """
  216.         _logger.debug("Entered in _cb_draw_resize_grip()")
  217.         self.get_style().paint_resize_grip(self.window, \
  218.                                gtk.STATE_NORMAL, \
  219.                                None, self, None, \
  220.                                gtk.gdk.WINDOW_EDGE_SOUTH_EAST, \
  221.                                config.icp_width - RESIZE_AREA_SIZE, \
  222.                                config.icp_height - RESIZE_AREA_SIZE, \
  223.                                RESIZE_AREA_SIZE, RESIZE_AREA_SIZE)
  224.  
  225.     def _cb_click_action(self, widget, event):
  226.         """
  227.         This is the callback for the button-release-event.
  228.  
  229.         If the button-release occurs around the coordinates of the preceding
  230.         button-press, it is considered to be a click (regardless of the
  231.         time passed between the button-press and button-release).  The
  232.         IconPalette gets hidden and the custom activated-event is emitted.
  233.         """
  234.         _logger.debug("Entered in _cb_click_action")
  235.         if not event.button == 1: # we are only interested in button 1 events
  236.             return
  237.         self._button1_pressed = False
  238.         self._is_press_in_resize_area = False
  239.         if abs(event.x_root - self._button1_press_x_pos) < DRAG_THRESHOLD \
  240.         and abs(event.y_root - self._button1_press_y_pos) < DRAG_THRESHOLD:
  241.             self.do_hide()
  242.             self.emit("activated")
  243.  
  244.     def do_show(self):
  245.         """Show the IconPalette at the correct position on the desktop."""
  246.         _logger.debug("Entered in do_show")
  247.         self.move(config.icp_x_position, config.icp_y_position) 
  248.         # self.move() is necessary; otherwise under some
  249.         # circumstances that I don't understand yet, the icp does not
  250.         # reappear where it disappeared (probably position in wm != position 
  251.         # in X)
  252.         self.show_all()
  253.  
  254.     def do_hide(self):
  255.         """Hide the IconPalette."""
  256.         _logger.debug("Entered in do_hide")
  257.         self.hide_all()
  258.  
  259.  
  260. if __name__ == "__main__":
  261.     iconPalette = IconPalette()
  262.     iconPalette.do_show()
  263.     gtk.main()
  264.  
  265.  
  266.  
  267.